package city.spring.modules.work.service.impl;

import city.spring.modules.work.entity.WorkReplaceApplyEntity;
import city.spring.modules.work.entity.WorkScheduleItemEntity;
import city.spring.modules.work.repository.WorkReplaceApplyRepository;
import city.spring.modules.work.service.WorkReplaceApplyService;
import city.spring.modules.work.service.WorkScheduleItemService;
import city.spring.utils.UserUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Service：值班替班申请表
 *
 * @author 侯坤林
 * @date 2020-06-28 10:24:34
 */
@CacheConfig(cacheNames = {WorkReplaceApplyServiceImpl.CACHE_NAME})
@Transactional(rollbackFor = Throwable.class)
@Service
public class WorkReplaceApplyServiceImpl extends ServiceImpl<WorkReplaceApplyRepository, WorkReplaceApplyEntity> implements WorkReplaceApplyService {
    private final WorkScheduleItemService scheduleItemService;

    public WorkReplaceApplyServiceImpl(WorkScheduleItemService scheduleItemService) {
        this.scheduleItemService = scheduleItemService;
    }

    @Override
    public void saveWorkReplaceApply(WorkReplaceApplyEntity entity) {
        // 设置为0标记待审核
        entity.setResult(0);
        Set<String> scheduleItemIds = new HashSet<>();
        scheduleItemIds.add(entity.getSourceId());
        scheduleItemIds.add(entity.getTargetWorkScheduleItemId());
        scheduleItemIds.remove(null);
        if (scheduleItemIds.isEmpty()) {
            throw new RuntimeException("请选择要替换的上班记录");
        }
        List<WorkReplaceApplyEntity> inDbList = lambdaQuery()
                .eq(WorkReplaceApplyEntity::getResult, 1)
                .and(wrapper -> {
                    wrapper.in(WorkReplaceApplyEntity::getSourceId, scheduleItemIds)
                            .or()
                            .in(WorkReplaceApplyEntity::getTargetWorkScheduleItemId, scheduleItemIds);
                })
                .list();
        if (!inDbList.isEmpty()) {
            Set<String> ids = inDbList.stream()
                    .flatMap(item -> Stream.of(item.getSourceId(), item.getTargetWorkScheduleItemId()))
                    .collect(Collectors.toSet());
            List<String> errorMsg = new ArrayList<>();
            if (ids.contains(entity.getSourceId())) {
                errorMsg.add("自己的班次已经在申请历史中");
            }
            if (ids.contains(entity.getTargetWorkScheduleItemId())) {
                errorMsg.add("别人的班次已经在申请历史中");
            }
            throw new RuntimeException(String.join("，", errorMsg) + "，选中的上班记录已经在系统中，请勿重复操作。");
        }
        // 我的上班记录
        WorkScheduleItemEntity sourceScheduleItem = getWorkScheduleItem(entity.getSourceId());
        // 别人的上班记录
        WorkScheduleItemEntity targetScheduleItem = getWorkScheduleItem(entity.getTargetWorkScheduleItemId());

        if (sourceScheduleItem != null) {
            // 有来源
            if (targetScheduleItem != null) {
                // 有目的：替班
                entity.setReplace();
                if (StringUtils.equals(sourceScheduleItem.getUserId(), targetScheduleItem.getUserId())) {
                    throw new RuntimeException("不能与自己的上班记录进行替换");
                }
                List<WorkScheduleItemEntity> list = scheduleItemService.lambdaQuery()
                        // .eq(WorkScheduleItemEntity::getWorkScheduleId, targetScheduleItem.getWorkScheduleId())
                        .eq(WorkScheduleItemEntity::getDate, targetScheduleItem.getDate())
                        .eq(WorkScheduleItemEntity::getWorkScheduleShiftId, targetScheduleItem.getWorkScheduleShiftId())
                        .eq(WorkScheduleItemEntity::getUserId, sourceScheduleItem.getUserId())
                        .eq(WorkScheduleItemEntity::getIsEnabled, true)
                        .list();
                if (!list.isEmpty()) {
                    throw new RuntimeException("想要替换的班次中已经存在自己的上班记录，无法继续完成替换申请");
                }
                list = scheduleItemService.lambdaQuery()
                        // .eq(WorkScheduleItemEntity::getWorkScheduleId, targetScheduleItem.getWorkScheduleId())
                        .eq(WorkScheduleItemEntity::getDate, sourceScheduleItem.getDate())
                        .eq(WorkScheduleItemEntity::getWorkScheduleShiftId, sourceScheduleItem.getWorkScheduleShiftId())
                        .eq(WorkScheduleItemEntity::getUserId, targetScheduleItem.getUserId())
                        .eq(WorkScheduleItemEntity::getIsEnabled, true)
                        .list();
                if (!list.isEmpty()) {
                    throw new RuntimeException("自己的班次中已经存在相同替换的人员，对方已经在我的班次中安排值班，无法继续完成替换申请");
                }
            } else {
                // 无目的，检查是否是新增上班
                if (entity.isAdd()) {
                    // 设置新增上班的字段状态
                    entity.setAdd();
                } else {
                    entity.setRest();
                    // TODO 预留：既不是替班，也不是新增，那就是休假
                    throw new RuntimeException("缺少参数，仅有原上班信息，无欲替换信息(1)");
                }
            }
            entity.setUserId(sourceScheduleItem.getUserId());
        } else {
            // 无来源
            if (targetScheduleItem != null) {
                // 有目的：替班。由于无来源，因此无法替班
                throw new RuntimeException("缺少参数，请填写原上班信息(2)");
            } else {
                // 无目的，检查是否是新增上班
                if (entity.isAdd()) {
                    // 设置新增上班的字段状态
                    entity.setAdd();
                } else {
                    // 既不是替班，也不是新增上班
                    throw new RuntimeException("缺少参数，请填写原上班、欲替换班次或上班位置(3)");
                }
                entity.setUserId(UserUtils.getPrincipal().getName());
            }
        }
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void updateWorkReplaceApply(WorkReplaceApplyEntity entity) {
        if (!updateById(entity)) {
            throw new RuntimeException("修改信息失败");
        }
    }

    @Override
    public void auditWorkReplaceApply(WorkReplaceApplyEntity entity) {
        Integer result = entity.getResult();
        String review = entity.getReview();
        if (result == null || result >= 3 || result <= 0) {
            // result 取值范围： -1 <= result <= 1，0表示待审核状态
            throw new RuntimeException("审核结果错误");
        }
        WorkReplaceApplyEntity findEntity = getById(entity.getId());
        if (findEntity == null) {
            throw new RuntimeException("未找到该申请记录");
        }
        // 设置审核结果和审核意见
        findEntity.setResult(result);
        findEntity.setReview(review);
        // 使用 updateById(entity); 修改数据时，将会修改 entity 对象中所有非null数据，如果某个字段为null，将会忽略该字段的修改
        boolean update = updateById(findEntity);
        if (!update) {
            throw new RuntimeException("审核失败");
        }
        if (result != 1) {
            // 审核不通过，不做任何处理
            return;
        }
        // 审核通过：修改值班表记录信息
        handleWorkScheduleItem(findEntity);
    }

    @Override
    public void deleteWorkReplaceApply(String primaryKey) {
        removeById(primaryKey);
    }

    @Override
    public void deleteWorkReplaceApply(List<String> primaryKeys) {
        removeByIds(primaryKeys);
    }

    /**
     * 处理被替换人的上班记录
     *
     * @param entity
     */
    private void handleWorkScheduleItem(WorkReplaceApplyEntity entity) {
        // 我的上班记录
        WorkScheduleItemEntity sourceScheduleItem = getWorkScheduleItem(entity.getSourceId());
        // 别人的上班记录
        WorkScheduleItemEntity targetScheduleItem = getWorkScheduleItem(entity.getTargetWorkScheduleItemId());

        if (sourceScheduleItem != null) {
            // 有来源
            disabledWorkScheduleItemEntity(sourceScheduleItem, 2);
            if (targetScheduleItem != null) {
                // 有目的：替班
                disabledWorkScheduleItemEntity(targetScheduleItem, 3);
                // 添加我的新的上班记录
                saveWorkScheduleItem(sourceScheduleItem.getUserId(),
                        targetScheduleItem.getWorkScheduleId(),
                        targetScheduleItem.getWorkScheduleShiftId(),
                        targetScheduleItem.getDate());
                // 添加别人的新的上班记录
                saveWorkScheduleItem(targetScheduleItem.getUserId(),
                        sourceScheduleItem.getWorkScheduleId(),
                        sourceScheduleItem.getWorkScheduleShiftId(),
                        sourceScheduleItem.getDate());
            } else {
                // 无目的，检查是否是新增上班
                if (entity.isAdd()) {
                    // 添加我的新的上班记录
                    saveWorkScheduleItem(sourceScheduleItem.getUserId(),
                            entity.getTargetWorkScheduleId(),
                            entity.getTargetWorkScheduleShiftId(),
                            entity.getTargetWorkScheduleDate());
                } else {
                    // TODO 为了维护逻辑结构，方便理解处理思路，请勿删除无用的逻辑判断，为后期维护功能提供便捷
                    // 预留：既不是替班，也不是新增，那就是休假
                    // 休假：在上方已经禁用了我的上班记录信息，因此不需要再次处理
                    // throw new RuntimeException("如果需要替班，则需要选择自己的上班记录和想要替换的上班记录");
                }
            }
        } else {
            // 无来源
            if (targetScheduleItem != null) {
                // TODO 为了维护逻辑结构，方便理解处理思路，请勿删除无用的逻辑判断，为后期维护功能提供便捷
                // 有目的：替班。由于无来源，因此无法替班
                // throw new RuntimeException("如果需要替班，则需要选择自己的上班记录和想要替换的上班记录");
            } else {
                // 无目的，检查是否是新增上班
                if (entity.isAdd()) {
                    // 添加我的新的上班记录
                    saveWorkScheduleItem(entity.getUserId(),
                            entity.getTargetWorkScheduleId(),
                            entity.getTargetWorkScheduleShiftId(),
                            entity.getTargetWorkScheduleDate());
                } else {
                    // TODO 为了维护逻辑结构，方便理解处理思路，请勿删除无用的逻辑判断，为后期维护功能提供便捷
                    // 既不是替班，也不是新增上班，因此忽略
                    // throw new RuntimeException("如果需要替班，则需要选择自己的上班记录和想要替换的上班记录");
                }
            }
        }

    }

    /**
     * 获取一个上班记录
     *
     * @param id 主键ID
     * @return {@link WorkScheduleItemEntity}
     */
    private WorkScheduleItemEntity getWorkScheduleItem(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }
        return scheduleItemService.lambdaQuery()
                .eq(WorkScheduleItemEntity::getId, id)
                .eq(WorkScheduleItemEntity::getIsEnabled, true)
                .one();
    }

    /**
     * 禁用上班记录
     *
     * @param entity {@link WorkScheduleItemEntity} 对象
     * @param status 状态值请查阅 {@link WorkScheduleItemEntity#status}
     */
    private void disabledWorkScheduleItemEntity(WorkScheduleItemEntity entity, Integer status) {
        if (entity != null) {
            // 禁用该上班记录
            entity.setIsEnabled(false);
            entity.setStatus(status);
            scheduleItemService.updateById(entity);
        }
    }

    /**
     * 保存上班记录信息
     *
     * @param userId              上班人ID
     * @param workScheduleId      排班表ID
     * @param workScheduleShiftId 上班班次ID
     * @param date                上班日期
     */
    private void saveWorkScheduleItem(String userId, String workScheduleId, String workScheduleShiftId, LocalDate date) {
        WorkScheduleItemEntity scheduleItemEntity = new WorkScheduleItemEntity();
        scheduleItemEntity.setUserId(userId);
        scheduleItemEntity.setIsEnabled(true);
        scheduleItemEntity.setStatus(1);
        scheduleItemEntity.setWorkScheduleId(workScheduleId);
        scheduleItemEntity.setWorkScheduleShiftId(workScheduleShiftId);
        scheduleItemEntity.setDate(date);
        scheduleItemService.save(scheduleItemEntity);
    }
}